package com.siashan.toolkit.api.security.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.siashan.toolkit.api.security.CryptoConst;
import com.siashan.toolkit.api.security.exception.RestCryptoException;
import com.siashan.toolkit.api.security.properties.RestCryptoProperties;
import com.siashan.toolkit.api.security.util.sign.SignUtil;
import com.siashan.toolkit.api.security.dto.RequestDto;
import org.springframework.http.HttpHeaders;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;

/**
 * 加解密验签工具类
 *
 * @author siashan
 * @since V1.0.1
 **/
public class RequestUtil {


    private static ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 验签及解析业务参数
     *
     * @param requestDto    请求dto
     * @param restCryptoProperties   配置文件
     * @return 报文流
     * @throws Exception  异常
     */
    public static InputStream decryptBody(RequestDto requestDto, RestCryptoProperties restCryptoProperties) throws Exception {

        Map<String, Object> requestMap = BeanUtil.beanToMap(requestDto);
        boolean flag = SignUtil.checkSignature(requestMap, restCryptoProperties);
        if (!flag) {
            throw new RestCryptoException("接口验签失败!");
        }
        InputStream inputStream = new ByteArrayInputStream(requestDto.getBizContent().getBytes());
        return inputStream;
    }


    /**
     * 获取请求Body
     *
     * @param bodyStream body输入流
     * @return body字符串
     */
    public static String getBodyString(InputStream bodyStream) {
        StringBuilder sb = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(bodyStream, Charset.forName(CryptoConst.UTF8)))) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }


    /**
     * 请求报文映射
     *
     * @param bodyStream 请求body输入流
     * @return RequestDto 对象
     * @throws IOException   异常
     */
    public static RequestDto getRequestDto(InputStream bodyStream) throws IOException {
        Map<String, Object> requestMap = getRequestMap(bodyStream);
        if (null == requestMap) {
            return null;
        }
        RequestDto requestDto = new RequestDto();
        requestDto.setAppId(requestMap.get(CryptoConst.FIELD_APPID).toString());
        requestDto.setBizContent(requestMap.get(CryptoConst.FIELD_BIZ_CONTENT).toString());
        requestDto.setSignType(requestMap.get(CryptoConst.FIELD_SIGN_TYPE).toString());
        requestDto.setSign(requestMap.get(CryptoConst.FIELD_SIGN).toString());
        requestDto.setNonceStr(requestMap.get(CryptoConst.FIELD_NONCE_STR).toString());
        requestDto.setTimeStamp(requestMap.get(CryptoConst.FIELD_TIME_STAMP).toString());
        return requestDto;
    }

    /**
     * 请求报文映射
     *
     * @param bodyStream 请求body输入流
     * @param headers    请求头
     * @return RequestDto 对象
     */
    public static RequestDto getRequestDto(InputStream bodyStream, HttpHeaders headers) {
        RequestDto requestDto = new RequestDto();
        requestDto.setAppId(headers.getFirst(CryptoConst.FIELD_APPID));
        requestDto.setTimeStamp(headers.getFirst(CryptoConst.FIELD_TIME_STAMP));
        requestDto.setNonceStr(headers.getFirst(CryptoConst.FIELD_NONCE_STR));
        requestDto.setSign(headers.getFirst(CryptoConst.FIELD_SIGN));
        requestDto.setSignType(headers.getFirst(CryptoConst.FIELD_SIGN_TYPE));
        requestDto.setBizContent(getBodyString(bodyStream));
        return requestDto;
    }


    /**
     * 请求报文映射
     *
     * @param bodyStream 请求body输入流
     * @return RequestDto 对象
     * @throws IOException  异常
     */
    public static Map<String, Object> getRequestMap(InputStream bodyStream) throws IOException {
        String bodyString = getBodyString(bodyStream);
        if (StrUtil.isBlank(bodyString.trim())) {
            return null;
        }
        return objectMapper.readValue(bodyString, Map.class);
    }


}
